package com.simulation.engine;

import com.simulation.model.DataPoint;
import com.simulation.model.SimulationTask;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.proxy.ProxyExecutable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class ScriptEngine {
    private static final Logger logger = LoggerFactory.getLogger(ScriptEngine.class);
    private final Map<String, Context> contextCache = new ConcurrentHashMap<>();

    /**
     * 执行数据点的模拟脚本
     * @param task 模拟任务
     * @param dataPoint 数据点
     * @param isAbnormal 是否为异常模拟
     * @return 计算结果
     */
    public Object executeScript(SimulationTask task, DataPoint dataPoint, boolean isAbnormal) {
        try {
            Context context = getOrCreateContext(task.getTaskId());
            
            // 设置获取值的函数，允许脚本访问其他数据点的值
            context.getBindings("js").putMember("getValue", (ProxyExecutable) arguments -> {
                if (arguments.length > 0) {
                    String pointName = arguments[0].asString();
                    Object value = task.getValue(pointName);
                    return value != null ? value : 0.0;
                }
                return 0.0;
            });

            // 设置当前时间戳
            context.getBindings("js").putMember("Date", context.eval("js", "Date"));
            
            // 选择执行的脚本
            String script = isAbnormal ? 
                dataPoint.getSimulationScript().getAbnormalScript() : 
                dataPoint.getSimulationScript().getNormalScript();
            
            if (script == null || script.trim().isEmpty()) {
                return dataPoint.getInitialValue();
            }

            Value result = context.eval("js", script);
            
            // 根据数据类型转换结果
            Object value = convertValue(result, dataPoint);
            
            logger.debug("Script executed for point {}: {} -> {}", 
                dataPoint.getPointName(), script, value);
            
            return value;
            
        } catch (PolyglotException e) {
            logger.error("Script execution error for point {}: {}", 
                dataPoint.getPointName(), e.getMessage());
            return dataPoint.getInitialValue();
        } catch (Exception e) {
            logger.error("Unexpected error executing script for point {}: {}", 
                dataPoint.getPointName(), e.getMessage(), e);
            return dataPoint.getInitialValue();
        }
    }

    /**
     * 获取或创建JavaScript执行上下文
     */
    private Context getOrCreateContext(String taskId) {
        return contextCache.computeIfAbsent(taskId, id -> {
            Context context = Context.newBuilder("js")
                .allowExperimentalOptions(true)
                .option("js.ecmascript-version", "2021")
                .build();
            
            // 注入Math对象
            context.eval("js", "var Math = globalThis.Math;");
            
            return context;
        });
    }

    /**
     * 根据数据点类型转换值
     */
    private Object convertValue(Value result, DataPoint dataPoint) {
        if (result.isNull()) {
            return dataPoint.getInitialValue();
        }

        try {
            switch (dataPoint.getDataType().toUpperCase()) {
                case "DOUBLE":
                case "FLOAT":
                    double doubleValue = result.asDouble();
                    if (dataPoint.getPrecision() != null && dataPoint.getPrecision() > 0) {
                        BigDecimal bd = new BigDecimal(doubleValue);
                        bd = bd.setScale(dataPoint.getPrecision(), RoundingMode.HALF_UP);
                        return bd.doubleValue();
                    }
                    return doubleValue;
                    
                case "INTEGER":
                case "INT":
                    return result.asInt();
                    
                case "LONG":
                    return result.asLong();
                    
                case "BOOLEAN":
                    return result.asBoolean();
                    
                case "STRING":
                    return result.asString();
                    
                default:
                    return result.asDouble();
            }
        } catch (Exception e) {
            logger.warn("Error converting value for point {}, using initial value: {}", 
                dataPoint.getPointName(), e.getMessage());
            return dataPoint.getInitialValue();
        }
    }

    /**
     * 清理任务的执行上下文
     */
    public void cleanupContext(String taskId) {
        Context context = contextCache.remove(taskId);
        if (context != null) {
            try {
                context.close();
                logger.debug("Cleaned up script context for task: {}", taskId);
            } catch (Exception e) {
                logger.warn("Error cleaning up script context for task {}: {}", taskId, e.getMessage());
            }
        }
    }

    /**
     * 清理所有上下文
     */
    public void cleanupAllContexts() {
        contextCache.forEach((taskId, context) -> {
            try {
                context.close();
            } catch (Exception e) {
                logger.warn("Error cleaning up context for task {}: {}", taskId, e.getMessage());
            }
        });
        contextCache.clear();
        logger.info("All script contexts cleaned up");
    }
} 